03. Quiz: Contour Features

Contour Features

Below is an image of two hands, making a thumbs up and down. If I perform contour detection on this image, I should get two distinct contours that define the outline of the hands and arms.

Your task will be to determine some information about the shape of these contours.

Two hands showing a thumbs up and thumbs down

Two hands showing a thumbs up and thumbs down

Select a single contour

Your first step in getting information from a list of multiple contours, is to select one at a time for analysis.

Assuming you've detected all the contours in an image, to select a single contour, simply select one by index from the list of all contours. Below, the first contour in a list is stored as selected_contour.

# Select the contour at index= 0 from a list
selected_contour = contours[0]

Then, to display this selected contour, you can use the same drawContours function that you've seen but instead of a -1 passed in as a parameter, you need to pass in the specific contour and an index value of 0.

# Draw the first contour (index = 0)
contour_image = cv2.drawContours(contour_image, [selected_contour], 0,  (0,255,0), 3)

# Draw all contours
contour image = cv2.drawContours(contour_image, contours, -1,  (0,255,0), 3)

Contour Features

Every contour has a number of features that you can calculate, including the area of the contour, it's orientation (the direction that most of the contour is pointing in), it's perimeter, and many other properties outlined in OpenCV documentation, here.

In this quiz, you'll be asked to identify the orientations of both the left and right hand contours. The orientation should give you an idea of which hand has its thumb up and which one has its thumb down!

Orientation

The orientation of an object is the angle at which an object is directed. To find the angle of a contour, you should first find an ellipse that fits this contour and then extract the angle from that shape.

# Fit an ellipse to a contour and extract the angle from that ellipse
(x,y), (MA,ma), angle = cv2.fitEllipse(selected_contour)

Note on Code Quizzes

  1. Some code will have been implemented for you, typically at the top of a quiz. The code you need to implement will be marked as TODO tasks.

  2. To see the image or text output of a quiz, click Test Run. In fact, it's always a good idea to test your code before you submit it!

  3. Finally, to see if your answer was correct, click Submit Answer. You can submit multiple times.

  4. You can restart this quiz and set it back it's original state at any time by pressing the Reset Quiz button.

Start Quiz:

import numpy as np
import matplotlib.pyplot as plt
import cv2

# Read in an image and convert to RGB
image = cv2.imread('thumbs_up_down.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Convert to grayscale
gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
# Create a binary thresholded image
retval, binary = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)

# Find contours from thresholded image
retval, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Draw all contours on a copy of the original image
contour_image = np.copy(image)
cv2.drawContours(contour_image, contours, -1, (0,255,0), 2)
plt.imshow(contour_image)
# ---------------------------------------------------------- #


## TODO: Complete this function so that 
## it returns the orientations of a list of contours
## The list should be in the same order as the contours
## i.e. the first angle should be the orientation of the first contour
def orientations(contours):
    """
    Orientation 
    :param cnt: a list of contours
    :return: the orientations of the contours
    """
    
    # Create an empty list to store the angles in
    # Tip: Use angles.append(value) to add values to this list
    angles = []
    
    return angles


# ---------------------------------------------------------- #
# Print out the orientation values
angles = orientations(contours)
print('Angles of each contour (in degrees): ' + str(angles))

Orientation values

These orientation values are in degrees measured from the x-axis. A value of zero means a flat line, and a value of 90 means that a contour is pointing straight up!

So, the orientation angles that you calculated for each contour should be able to tell us something about the general position of the hand. The hand with it's thumb up, should have a higher (closer to 90 degrees) orientation than the hand with it's thumb down.

Using this knowledge, your next task will be to focus only on the left hand. Keep scrolling!

Bounding Rectangle

In the next quiz, you'll be asked to find the bounding rectangle around the left hand contour, which has its thumb up, and use a bounding rectangle to crop the image and better focus on that one hand!

# Find the bounding rectangle of a selected contour
x,y,w,h = cv2.boundingRect(selected_contour)

# Draw the bounding rectangle as a purple box
box_image = cv2.rectangle(contour_image, (x,y), (x+w,y+h), (200,0,200),2)

And to crop the image, select the correct width and height of the image to include.

# Crop using the dimensions of the bounding rectangle (x, y, w, h)
cropped_image = image[y: y + h, x: x + w] 

Start Quiz:

import numpy as np
import matplotlib.pyplot as plt
import cv2

# Read in an image and convert to RGB
image = cv2.imread('thumbs_up_down.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Convert to grayscale
gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
# Create a binary thresholded image
retval, binary = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)

# Find contours from thresholded image
retval, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# ---------------------------------------------------------- #


## TODO: Complete this function so that
## it returns a new, cropped version of the original image
def left_hand_crop(image, selected_contour):
    """
    Left hand crop 
    :param image: the original image
    :param contours: the contour that will be used for cropping
    :return: the cropped image around the left hand
    """
    
    ## TODO: Detect the bounding rectangle of the left hand contour
    
    ## TODO: Crop the image using the dimensions of the bounding rectangle
    # Make a copy of the image to crop
    cropped_image = np.copy(image)
    
    return cropped_image


## TODO: Select the left hand contour from the list
## Replace this value
selected_contour = None


# ---------------------------------------------------------- #
# If you've selected a contour
if(selected_contour is not None):
    # Call the crop function with that contour passed in as a parameter
    cropped_image = left_hand_crop(image, selected_contour)
    
    # Display the cropped image side-by-side with the original
    f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
    f.tight_layout()
    ax1.imshow(image)
    ax1.set_title('Original Image')
    ax2.imshow(cropped_image)
    ax2.set_title('Cropped Image')